package com.study.java8;

import org.apache.log4j.Logger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static java.lang.Thread.sleep;

public class CompletableFutureStudy2 {
    private static Logger logger = Logger.getLogger(CompletableFutureStudy2.class);
    public static void main(String[] args) throws Exception {
//        completableFutureTest();
//        completableFutureAllOfTest();
//        completableFutureAnyOfTest();
//        completableFutureThenApplyTest();
//        completableFutureAsyncTest();
//        completableFutureThenAcceptTest();
//        completableFutureThenCombineTest();
//        completableFutureApplyToEitherTest();
        completableFutureWhenCompleteTest();
    }


    public static void completableFutureTest() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello world");
        try {
            System.out.println(completableFuture.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * allOf方法创建的CompletableFuture以多个CompletableFuture的结果全部返回作为其执行完成的标志，但其本身并不作为任何结果的载体
     */
    public static void completableFutureAllOfTest() {
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world1 start!");
            try {
                sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hello world1 wake!");
            return "Hello world1 finished!";
        });
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world2 start!");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hello world2 wake!");
            return "Hello world2 finished!";
        });
        CompletableFuture completableFutureAll = CompletableFuture.allOf(completableFuture1, completableFuture2);
        try {
            System.out.println(completableFutureAll.get());
            System.out.println(completableFutureAll.getNow("默认值"));
            System.out.println(completableFuture1.get());
            System.out.println(completableFuture2.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    /*
    输出：
    Hello world1 start!
    Hello world2 start!
    Hello world2 wake!
    Hello world1 wake!
    null */

    /**
     * anyOf方法创建的CompletableFuture以多个CompletableFuture中的其中任意一个返回作为其完成的标志
     */
    public static void completableFutureAnyOfTest() {
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world1 start!");
            try {
                sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hello world1 wake!");
            return "Hello world1 finished!";
        });
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world2 start!");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hello world2 wake!");
            return "Hello world2 finished!";
        });
        CompletableFuture completableFutureAny = CompletableFuture.anyOf(completableFuture1, completableFuture2);
        try {
            System.out.println(completableFutureAny.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    /*输出：
    Hello world1 start!
    Hello world2 start!
    Hello world2 wake!
    Hello world2 finished!
    Hello world1 wake!*/

    /**
     * 当该CompletableFuture执行完成时会继续执行传入thenApply方法的函数式接口
     */
    public static void completableFutureThenApplyTest() {
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world1 start!");
            return "Hello world1 finished!";
        }).thenApply(s -> {
            System.out.println(s + " Hello world2 start!");
            return "Hello world2 finished!";
        });
        try {
            System.out.println(completableFuture1.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    private static Integer i = 0;

    public static void completableFutureAsyncTest() throws Exception {



        /*System.out.println("0 " + Thread.currentThread());
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("1 " + "supplyAsync " + Thread.currentThread());
            return "";
        }).thenApply(s -> {
            try {
                //只要slee是1000，就会立马结束；其他任何数字都没问题
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("1 " + "thenApply " + Thread.currentThread());
            return "";
        });

        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("2 " + "supplyAsync " + Thread.currentThread());
            return "";
        }).thenApplyAsync(s -> {
            try {
                //只要slee是1000，就会立马结束；其他任何数字都没问题
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("2 " + "thenApplyAsync " + Thread.currentThread());
            return "";
        });

        sleep(101);*/
    }

    /*输出：
            0 Thread[main,5,main]
            1 supplyAsync Thread[ForkJoinPool.commonPool-worker-9,5,main]
            1 thenApply Thread[main,5,main]
            2 supplyAsync Thread[ForkJoinPool.commonPool-worker-9,5,main]
            2 thenApplyAsync Thread[ForkJoinPool.commonPool-worker-9,5,main]*/

    /**
     * thenApply支持单输入单输出，因为thenApply方法的参数为Function类型的函数式接口，是既有输入又有输出的；
     * 而thenAccept则只支持单输入而不能输出，因为thenAccept方法的参数为Consumer类型的函数式接口，只有输入没有输出；
     * thenRun则即无输入也无输出
     输出：
     Hello world1 start!
     Hello world1 finished! Hello world2 start!
     null
     */
    public static void completableFutureThenAcceptTest() {
        CompletableFuture<Void> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world1 start!");
            return "Hello world1 finished!";
        }).thenAccept(s -> {
            System.out.println(s + " Hello world2 start!");
        });

        CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(()->{
            System.out.println("completableFuture2 start!");
            return "completableFuture2 finished!";
        }).thenRun(()->{
            System.out.println(" OVER!");
        });
        try {
            System.out.println("completableFuture1.get()："+completableFuture1.get());
            System.out.println("completableFuture2.get()："+completableFuture2.get());
            CompletableFuture anyOf = CompletableFuture.anyOf(completableFuture1);
            System.out.println(anyOf.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * 输出：
     Hello world1 start!
     Hello world2 start!
     Hello world combine start!
     Hello world1 finished! Hello world2 finished!
     */
    public static void completableFutureThenCombineTest() {
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(()->{
            System.out.println("Hello world1 start!");
            return "Hello world1 finished!";
        });

        CompletableFuture completableFuture2= CompletableFuture.supplyAsync(()->{
            System.out.println("Hello world2 start!");
            return "Hello world2 finished!";
        }).thenCombine(completableFuture1, (r2, r1)->{
            System.out.println("Hello world combine start!");
            return r1 + " " + r2;
        });
        try {
            System.out.println(completableFuture2.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * 输出：
     Hello world1 start!
     Hello world2 start!
     Hello world2 wake!
     Hello world applyToEither start!
     Hello world2 finished!
     Hello world1 wake!
     */
    public static void completableFutureApplyToEitherTest() throws InterruptedException {
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world1 start!");
            try {
                sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hello world1 wake!");
            return "Hello world1 finished!";
        });
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Hello world2 start!");
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hello world2 wake!");
            return "Hello world2 finished!";
        }).applyToEither(completableFuture1, (r) -> {
            System.out.println("Hello world applyToEither start!");
            return r;
        });
        try {
            System.out.println(completableFuture2.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        sleep(100);
    }


    /**
     * 输出：
     result1:success
     exception1:null
     result2:null
     exception2:java.util.concurrent.CompletionException: java.lang.RuntimeException
     get1:success
     java.util.concurrent.ExecutionException: java.lang.RuntimeException
     at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
     at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
     */
    public static void completableFutureWhenCompleteTest() {
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            if (true) {
                return "success";
            } else {
                throw new RuntimeException();
            }
        }).whenComplete((r, e) -> {
            System.out.println("result1:" + r);
            System.out.println("exception1:" + e);
        });

        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            if (false) {
                return "success";
            } else {
                throw new RuntimeException();
            }
        }).whenComplete((r, e) -> {
            System.out.println("result2:" + r);
            System.out.println("exception2:" + e);
        });

        try {
            System.out.println("get1:" + completableFuture1.get());
            System.out.println("get2:" + completableFuture2.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }



    public void selfTest() throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> test1 = CompletableFuture.supplyAsync(()->{
            for (int j=0;j<8;j++){
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i+=1;
                System.out.println("1---："+i);
            }
            return i;
        });

        CompletableFuture<Integer> test2 = CompletableFuture.supplyAsync(()->{
            for (int j=0;j<10;j++){
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i+=1;
                System.out.println("---2："+i);
            }
            return i;
        });

        CompletableFuture allof = CompletableFuture.anyOf(test1, test2);
        System.out.println("allof.get()："+allof.get());
        System.out.println("test1.get()："+test1.get());
        System.out.println("test2.get()："+test2.get());
    }


}